package DyingBaby.backend.table;

import DyingBaby.backend.catalog.ColumnDef;
import DyingBaby.backend.catalog.ColumnType;
import DyingBaby.backend.catalog.TableSchema;
import com.google.gson.Gson; // 引入Gson库用于JSON序列化和反序列化

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

/**
 * TableHeap 类实现了一个简单的基于文件的表存储引擎。
 * 它将表中的每一行记录作为JSON字符串存储在一个文本文件中，每行一条记录。
 * 它适用于小型、简单的数据存储需求。
 */
public class TableHeap {
    // 数据存储目录
    private static final String DATA_DIR = "data";
    // 表数据文件的后缀
    private static final String TABLE_SUFFIX = ".tbl";

    private final Gson gson = new Gson(); // Gson实例，用于JSON转换
    private final TableSchema schema; // 表的模式定义
    private final Path tablePath; // 表数据文件的路径

    /**
     * 构造函数：初始化表存储
     * @param schema 表的模式定义
     */
    public TableHeap(TableSchema schema) {
        this.schema = schema;
        try {
            // 确保数据目录存在
            Path dataDir = Paths.get(DATA_DIR);
            if (!Files.exists(dataDir)) {
                Files.createDirectories(dataDir);
            }
            // 构建表数据文件路径：data/表名.tbl
            this.tablePath = dataDir.resolve(schema.getTableName() + TABLE_SUFFIX);
            File f = tablePath.toFile();
            // 如果文件不存在，则创建它
            if (!f.exists()) {
                f.createNewFile();
            }
        } catch (Exception e) {
            throw new RuntimeException("Init table heap failed", e);
        }
    }

    /**
     * 插入一条记录到表中
     * @param record 要插入的记录，是一个Map，键为列名，值为对应的数据
     */
    public synchronized void insert(Map<String, Object> record) {
        // 验证记录是否符合表模式定义
        validateRecord(record);
        try (BufferedWriter bw = new BufferedWriter(new FileWriter(tablePath.toFile(), true))) {
            // 将记录转换为JSON字符串并写入文件（追加模式）
            bw.write(gson.toJson(record));
            bw.newLine(); // 写入换行符，使每条记录占一行
        } catch (Exception e) {
            throw new RuntimeException("Insert failed", e);
        }
    }

    /**
     * 扫描表中的记录，可选地应用过滤条件
     * @param filter 可选的过滤条件，如果为null则返回所有记录
     * @return 符合条件的记录列表
     */
    public synchronized List<Map<String, Object>> scan(Predicate<Map<String, Object>> filter) {
        List<Map<String, Object>> out = new ArrayList<>();
        try (BufferedReader br = new BufferedReader(new FileReader(tablePath.toFile()))) {
            String line;
            // 逐行读取文件
            while ((line = br.readLine()) != null) {
                if (line.isEmpty()) continue; // 跳过空行
                // 将JSON字符串解析为Map对象
                @SuppressWarnings("unchecked")
                Map<String, Object> rec = gson.fromJson(line, LinkedHashMap.class);
                // 如果没有过滤条件，或者记录满足过滤条件，则添加到结果列表
                if (filter == null || filter.test(rec)) {
                    out.add(rec);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Scan failed", e);
        }
        return out;
    }

    /**
     * 验证记录是否符合表模式定义
     * @param record 要验证的记录
     */
    private void validateRecord(Map<String, Object> record) {
        Map<String, ColumnDef> cols = schema.getColumnMap();
        // 检查记录是否包含所有必需的列
        for (ColumnDef cd : cols.values()) {
            if (!record.containsKey(cd.getName())) {
                throw new IllegalArgumentException("Missing column: " + cd.getName());
            }
            Object v = record.get(cd.getName());
            // 验证数据类型
            if (cd.getType() == ColumnType.INT) {
                if (!(v instanceof Number)) {
                    throw new IllegalArgumentException("Column " + cd.getName() + " expects INT");
                }
            } else if (cd.getType() == ColumnType.STRING) {
                if (!(v instanceof String)) {
                    throw new IllegalArgumentException("Column " + cd.getName() + " expects STRING");
                }
                Integer ml = cd.getMaxLength();
                // 验证字符串长度是否超过最大长度限制
                if (ml != null && ((String) v).length() > ml) {
                    throw new IllegalArgumentException("Column " + cd.getName() + " exceeds max length " + ml);
                }
            }
        }
        // 检查记录是否包含额外的列
        if (record.size() != cols.size()) {
            throw new IllegalArgumentException("Unexpected columns in record");
        }
    }
} 